package com.koron.common.core.business.readrecord;

import com.koron.common.core.business.common.service.UserReadRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;

/**
 * @author twcao
 * @title: ReadRecordAspect
 * @projectName GDH_EAM_V2
 * @description: 用户已读标识切面
 * @date 2021/12/1616:55
 */
@Slf4j
@Aspect
@Component
public class ReadRecordAspect {

    @Autowired
    private UserReadRecordService recordService;

    @Pointcut("@annotation(com.koron.common.core.business.readrecord.ReadRecord)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object after(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        ReadRecord methodAnno = method.getAnnotation(ReadRecord.class);
        Parameter[] parameters = method.getParameters();
        String readId = null;
        Boolean insert = methodAnno.insert();
        Boolean echo = methodAnno.echo();
        Boolean skip = methodAnno.skip();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            // 1、获取方法参数上的业务id
            if(parameter.isAnnotationPresent(ReadRecord.class)) {
                readId = String.valueOf(args[i]);
                break;
            }
            // 2、获取嵌套在实体内的业务id，但业务id需要添加注解ReadRecord
            if(!ClassUtils.isPrimitiveOrWrapper(parameter.getClass())) {
                readId = getReadId(args[i]);
            }
            if(StringUtils.isNotBlank(readId)) {
                break;
            }
        }
        Object proceed = pjp.proceed();
        if(insert && !skip) {
            recordService.insert(readId);
        }
        if(echo) {
            resolveMark(proceed, readId);
        }
        return proceed;
    }

    private String getReadId(Object obj) {
        if(obj == null) {
            return "";
        }
        if(ClassUtils.isPrimitiveOrWrapper(obj.getClass())) {
            return "";
        }
        Field[] fields = FieldUtils.getAllFields(obj.getClass());
        for (int i = 0, size = fields.length; i < size; i++) {
            Field field = fields[i];
            field.setAccessible(true);
            Object fieldVal = null;
            try {
                fieldVal = field.get(obj);
            } catch (Exception e) {
                continue;
            }
            if(fieldVal == null) {
                continue;
            }
            if(ClassUtils.isPrimitiveOrWrapper(fieldVal.getClass()) && !field.isAnnotationPresent(ReadRecord.class)) {
                continue;
            }
            if(ClassUtils.isPrimitiveOrWrapper(fieldVal.getClass()) || fieldVal instanceof String) {
                return String.valueOf(fieldVal);
            } else {
                return getReadId(fieldVal);
            }
        }
        return "";
    }

    private void resolveMark(Object result, String readId) {
        Field[] fields = FieldUtils.getAllFields(result.getClass());
        for (int i = 0, size = fields.length; i < size; i++) {
            try {
                Field field = fields[i];
                field.setAccessible(true);
                Object fieldVal = FieldUtils.readField(field, result);
                if(ClassUtils.isPrimitiveOrWrapper(fieldVal.getClass()) || field.getType() == String.class) {
                    continue;
                }
                // 如果是单个实例
                if(Arrays.asList(ClassUtils.getAllInterfaces(fieldVal)).contains(Read.class)) {
                    try {
                        Read read = (Read) field.get(result);
                        read.setRead(recordService.getByBizId(readId));
                        return;
                    } catch (Exception e) {
                        return;
                    }
                }
                // 如果是集合
                if(Arrays.asList(ClassUtils.getAllInterfaces(fieldVal)).contains(Collection.class)) {
                    try {
                        Collection list = (Collection) field.get(result);
                        list.stream().forEach(row -> recordService.mark(list));
                        return;
                    } catch (Exception e) {
                        return;
                    }
                }
                resolveMark(fieldVal, readId);
            } catch (Exception e) {
                return;
            }
        }
    }
}
